#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Osh.Dvb
{
	/// <summary>
	/// PsiStream class decodes source stream of TS packet payloads (184-byte TS packets without 4-byte header)
	/// into packetized PSI section stream.
	/// </summary>
	public class PsiStream : PacketStream
	{
		private const int SourcePacketSize = 184;
		private PacketStream _sourceStream;
		private bool _checkCrc32;

		/// <summary>
		/// Constructs PsiStream class.
		/// </summary>
		/// <param name="stream">Stream of 184-byte packets.</param>
		public PsiStream(Stream stream, bool checkCrc32)
		{
			_sourceStream = new PacketStreamWrapper(stream, SourcePacketSize);
			_checkCrc32 = checkCrc32;
		}

		protected override bool MoveToNextPacketInternal()
		{
			this.PacketSize = 0;

			while(true)
			{
				if(_sourceStream.PacketPosition >= _sourceStream.PacketSize)
				{
					if(!_sourceStream.MoveToNextPacket())
						return false;
				
					// Check that pointer_field is 0x00.
					if(_sourceStream.PacketBuffer[0] != 0)
					{
						_sourceStream.PacketSeek(0, SeekOrigin.End);
						continue;
					}

					_sourceStream.PacketSeek(1, SeekOrigin.Current); // skip pointer_field
				}

				byte[] sourcePacket = _sourceStream.PacketBuffer;
				int sourcePacketPosition = _sourceStream.PacketPosition;

				// Check that table_id value is not forbidden (<0xFF), otherwise move to the next packet.
				if(sourcePacket[sourcePacketPosition] == 0xFF)
				{
					_sourceStream.PacketSeek(0, SeekOrigin.End);
					continue;
				}

				int sectionLength = ((sourcePacket[sourcePacketPosition + 1] & 0xF) << 8)
					| sourcePacket[sourcePacketPosition + 2]; // 12-bit section_length
				int fullSectionLength = 3 + sectionLength;
				
				EnsurePacketBufferSize(fullSectionLength);
				this.PacketSize = fullSectionLength;

				// Read current packet.
				int toRead = Math.Min(_sourceStream.PacketSize - sourcePacketPosition, fullSectionLength);
				Buffer.BlockCopy(sourcePacket, sourcePacketPosition, this.PacketBuffer, 0, toRead);
				// Move source packet position because we can use the rest of this packet next time.
				_sourceStream.PacketSeek(toRead, SeekOrigin.Current);
				
				// Read next packets until the whole section is read.
				int filledSectionLength = toRead;
				while(filledSectionLength < fullSectionLength)
				{
					if(!_sourceStream.MoveToNextPacket())
						return false;

					toRead = Math.Min(_sourceStream.PacketSize, fullSectionLength - filledSectionLength);
					Buffer.BlockCopy(_sourceStream.PacketBuffer, 0, this.PacketBuffer, filledSectionLength, toRead);
					// Move source packet position because we can use the rest of this packet next time.
					_sourceStream.PacketSeek(toRead, SeekOrigin.Current);
					filledSectionLength += toRead;
				}

				if(_checkCrc32 && !DvbUtil.CheckCrc32(this.PacketBuffer, 0, this.PacketSize))
				{
					this.PacketSize = 0;
					// We cannot relay on the rest of the source packet. Let's move on.
					_sourceStream.PacketSeek(0, SeekOrigin.End);
					continue;
				}
				break;
			}
			return true;
		}

		public override void Close()
		{
			base.Close();
			_sourceStream.Close();
		}

	}
}
